[進階 js 12] Event Loop


Posted by tzutzu858 on 2021-03-31

這篇文章主要是做 所以說 event loop 到底是什麼玩意兒?| Philip Roberts | JSConf EU 這個影片的筆記,這篇文章所放的圖片也是影片擷取出來

JavaScript , What are you ?

JavaScript 是個單執行緒的程式語言,裡面有
Call stack (呼叫堆疊)、Event Loop (事件循環)、Callback queue (回調佇列) 和一些 API 等等


那 V8(Google 開發的開源 JavaScript 引擎)也有 Call stackEvent LoopCallback queue 和其它 API 嗎?
V8 說我有 Call stackheap,其他是什麼?(疑!!!)

來看一下 JavaScript 的 Runtime, 像是 Chrome 與 Node.js 都使用了 V8 引擎

JavaScript Runtime的簡化示意圖

V8 有 Heap(memory allocation),call stack(execution contexts)
但當你用 setTimeout 或是 DOM 或是 HTTP 請求的東西,那些並不存在在 V8 裡
所以不存在 V8 ,那他們又是怎麼運行的?


那些像是 DOM、AJAX、setTimeout、Event Loop、callback queue 之類的東西,都是 web API ,由瀏覽器所提供額外的東西

就讓我們從頭講起吧

What is the call stack

one thread == one call stack == one thing at a time

Call stack 是執行堆疊,在調用多個函數的腳本中,跟踪其位置的機制。
一旦完成該功能,它將從 stack 的頂部移走,直到沒有剩餘為止。


blowing the stack

也許你有聽過 blowing the stack,像是下面例子

function foo () {
    return foo ();
}

foo();


然後 Chrome 會說 :

RangeError: Maximum call stack size exceeded


blocking

blocking 沒有一個嚴格的定義
它其實就是一段很慢的程式
跑起來很慢、同時又在 stack 上的東西就是 blocking

var foo = $.getSync('//foo.com');
var foo = $.getSync('//bar.com');
var foo = $.getSync('//qux.com');

console.log(foo);
console.log(bar);
console.log(qux);

如果上述是同步請求怎辦
那這樣呼叫 $.getSync('//foo.com') ,然後網路請求,等待回應...
好了以後再呼叫 $.getSync('//bar.com') ,然後網路請求,等待回應...
好了以後再呼叫 $.getSync('//qux.com') ,然後網路請求,等待回應...
最後好不容易那三個 blocking 已完成,可以清空 stack,再做 console.log(foo)console.log(bar)console.log(qux)

在單一執行緒的程式語言並不能像 Ruby 那樣使用多執行緒,我們做了一個網路請求就只能等它跑完為止,因為並沒有處理這種情況的方法
那如果我們有同步請求,那我們就不能做其他事,無法顯示畫面,無法跑其他程式碼都卡住了,但我希望使用者看到流暢 UI ,那我們就不能 block the stack

最簡單的解法就是

asynchronous callbacks


瀏覽器中幾乎沒有 Blocking ,在 Node.js 也是一樣,都是 Async (非同步)
基本上就是執行一些程式碼然後 callback,然後再執行,那實際上是怎樣呢?

console.log('a')

setTimeout(
    function cb(){
        console.log('b')
    }, 1000)

console.log('c')

按照同步程式的執行順序,應該是 a -> b -> c 這個結果,但真正的結果是 a -> c -> b,那到底發生了甚麼事?

畫面不夠清楚可以直接到 loupe 網站來執行看看
loupe 是視覺化的呈現 JavaScript 的 call stack / event loop / callback queue 如何相互影響,有視覺化真的比較好理解

這基本上就是 Event Loop 在 Concurrency 上會發揮作用的地方

就算把上述例子的 setTimeout 變成 0 呢 ? 那執行順序會是如何 ? 結果依然是 a -> c -> b
不要忘了 Event Loop 的特性是必須等到 stack 清空後才可以把 callback 放到 stack ,才繼續執行 console.log('b')


今天就算是執行 AJAX ,它也是會放到 web apis ,在 XHR 等待回應前,或是永遠不會完成也沒關係,stack 都可以順利繼續執行程式碼不會塞住。假設執行完,它就會被放到 task queue,然後再被 Event Loop 抓到 stack 裡。


回顧為何要有 Event Loop ?

一開頭便講了 JavaScript 是個單執行緒的程式語言,一次只能做一件事,你在做其他程式時,他無法做出 AJAX 請求,你在做其他程式時,他無法做出 setTimeout 的請求,讓我們能夠同時做事的原因是 :

瀏覽器不只有 Runtime ,瀏覽器會提供我們其他東西,那些都是你無法取得的執行緒,你只能呼叫它們

而當那些 Web Apis 執行完,它就會被放到 task queue。
之後 stack 被清空後,task queue 裡的事件就會被依序推回 stack。

這個偵測 stack 為空,便把 task queue 內的事件推回給 stack 的機制就稱為 Event Loop。

簡單來說 Event Loop 的工作在於 :

  1. 查看 stack 和查看 task queue
  2. 如果 stack 是空的,他就會把位於 task queue 的第一個東西放在 stack,讓它 run

參考資料 :
所以說 event loop 到底是什麼玩意兒?| Philip Roberts | JSConf EU
JavaScript 中的同步與非同步(上):先成為 callback 大師吧!
異步程式設計與事件迴圈
What is an event loop?
Concurrency與Parallelism的不同之處










Related Posts

使用 Python 進行中斷點 step by step debug 除錯

使用 Python 進行中斷點 step by step debug 除錯

7. 濾波器設計

7. 濾波器設計

基礎 HTML &  CSS

基礎 HTML & CSS


Comments